package main

import (
	"fmt"
	"log"
	"os"
	"io"
	"path"
	"encoding/binary"
)

// from: http://ulisse.elettra.trieste.it/services/doc/dbase/DBFstruct.htm
type tableHeader struct {
	Versioning byte
	LastUpdate [3]byte
	NRecs uint32
	HeaderSize uint16
	RecSize uint16
	DoesntMatter [20]byte
}

func main() {
	var th tableHeader
	if len(os.Args) != 3 {
		fmt.Println("Convert dbf files to fixed-width records, one per line.")
		fmt.Println("Usage:", path.Base(os.Args[0]), "input.dbf output")
		os.Exit(1)
	}
	fi, err := os.Open(os.Args[1])
	if err != nil {
		log.Fatal(err)
	}
	defer fi.Close()
	fo, err := os.Create(os.Args[2])
	if err != nil {
		log.Fatal(err)
	}
	defer fo.Close()
	err = binary.Read(fi, binary.LittleEndian, &th)
	if err != nil {
		log.Fatal(err)
	}
	hs := make([]byte, th.HeaderSize)
	_, err = fi.Seek(0, 0)
	if err != nil {
		log.Fatal(err)
	}
	// skip the header, we want the data
	err = binary.Read(fi, binary.LittleEndian, &hs)
	if err != nil && err != io.EOF {
		log.Fatal(err)
	}
	rs := make([]byte, th.RecSize)
	for i:=th.NRecs; i>0; i-- {
			err = binary.Read(fi, binary.LittleEndian, &rs)
			if err != nil && err != io.EOF {
				log.Fatal(err)
			}
			// write record adding a newline
			err := binary.Write(fo, binary.LittleEndian, append(rs, 0x0a))
			if err != nil {
				log.Fatal(err)
			}
	}
}
